package com.suray.wcs.service.core.util;

import com.suray.basic.wcs.utils.LoggerUtil;
import okio.BufferedSink;
import okio.BufferedSource;
import okio.Okio;
import okio.Timeout;

import java.io.*;
import java.net.Socket;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.concurrent.TimeUnit;

public class SocketSendUtil {
    private static Hashtable<String, SocketSendUtil> socketMap = new Hashtable<>();//储存实例的容器
    private String ip;
    private int port;

    final byte WEIGHT[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x02};

    /**
     * 构造函数
     *
     * @param ip
     * @param port
     */
    private SocketSendUtil(String ip, int port) {
        this.ip = ip;
        this.port = port;
    }

    public synchronized static SocketSendUtil getSocketSendUtil(String ip, int port) {
        String key = getKey(ip, port);
        if (socketMap == null) {
            socketMap = new Hashtable<>();
        }
        if (!socketMap.containsKey(key)) {
            SocketSendUtil socketSendUtil = new SocketSendUtil(ip, port);
            socketMap.put(key, socketSendUtil);
            return socketSendUtil;
        } else {
            return socketMap.get(key);
        }
    }

    /**
     * 获取称重设备的重量值
     *
     * @return
     */
    public int getWeight() {
        int count = 0;
        int weight = 0;
        while (count < 3) {
            weight = weightRead();
            if (weight > 0) {
                break;
            }
            count++;
        }

        return weight;
    }

    /**
     * 开启扫码专用send
     *
     * @return
     * @throws IOException
     */
    public void startScanner() throws IOException {
        Socket socket = null;
        BufferedSource in = null;
        BufferedSink out = null;
        try {
            socket = new Socket(ip, port);
            in = Okio.buffer(Okio.source(socket));
            out = Okio.buffer(Okio.sink(socket));
            Timeout timeout = in.timeout();
            timeout.timeout(10, TimeUnit.SECONDS);
            byte[] bs = "START".getBytes("UTF-8");
            out.write(bs);
            out.flush();
        } finally {
            closeStream(socket, in, out);
        }

    }

    /**
     * 关闭扫码专用receive
     *
     * @return
     * @throws IOException
     */
    public byte[] closeScanner() throws IOException {
        Socket socket = null;
        BufferedSource in = null;
        BufferedSink out = null;
        try {
            socket = new Socket(ip, port);
            in = Okio.buffer(Okio.source(socket));
            out = Okio.buffer(Okio.sink(socket));
            Timeout timeout = in.timeout();
            timeout.timeout(10, TimeUnit.SECONDS);
            byte[] bs = "STOP".getBytes("UTF-8");
            out.write(bs);
            out.flush();
            //定义最大数组
            return getResultBytes(in);
        } finally {
            closeStream(socket, in, out);
        }
    }

    /**
     * 获取流中的结果
     *
     * @param in
     * @return
     * @throws IOException
     */
    private byte[] getResultBytes(BufferedSource in) throws IOException {
        byte[] buff = new byte[1024];
        int maxNum = in.read(buff);

        if (maxNum < 0) {
            throw new IOException("通讯异常：readData size = " + maxNum);
        }

        byte[] b = new byte[maxNum];
        System.arraycopy(buff, 0, b, 0, maxNum);
        return b;
    }

    /**
     * read 自动上传条码值类型扫码程序专用
     *
     * @return
     * @throws IOException
     */
    public byte[] getReturnBytes() throws IOException {
        Socket socket = null;
        BufferedSource in = null;
        try {
            socket = new Socket(ip, port);
            in = Okio.buffer(Okio.source(socket));
            Timeout timeout = in.timeout();
            timeout.timeout(10, TimeUnit.SECONDS);
            //定义最大数组
            return getResultBytes(in);
        } finally {
            closeStream(socket, in, null);
        }

    }


    public synchronized byte[] send(byte[] bs) throws IOException {
        Socket socket = null;
        BufferedSource in = null;
        BufferedSink out = null;
        try {
            socket = new Socket(ip, port);
            in = Okio.buffer(Okio.source(socket));
            out = Okio.buffer(Okio.sink(socket));
            Timeout timeout = in.timeout();
            timeout.timeout(10, TimeUnit.SECONDS);
            socket = new Socket(ip, port);
            out.write(bs);
            out.flush();
            return getResultBytes(in);
        } finally {
            closeStream(socket, in, out);
        }

    }

    private int weightRead() {
        SocketSendUtil socketSendUtil = getSocketSendUtil(ip, port);
        try {
            byte[] data = socketSendUtil.send(WEIGHT);
            byte[] weight = Arrays.copyOfRange(data, 9, 13);
            return combineToDWord(weight[3], weight[2], weight[1], weight[0]) / 10;
        } catch (Exception e) {
            LoggerUtil.error("获取称重信息异常，原因：" + e.getMessage(), this.getClass());
            return 0;
        }
    }

    /**
     * 4个字节组合成DWord
     *
     * @param low
     * @param midLow
     * @param midHigh
     * @param high
     * @return
     */
    public static int combineToDWord(byte low, byte midLow, byte midHigh, byte high) {
        return (((int) high << 24) & 0xFFFFFFFF) + (((int) midHigh << 16) & 0xFFFFFF) + (((int) midLow << 8) & 0xFFFF) + (low & 0xFF);
    }

    /**
     * 手动关闭流
     *
     * @param socket
     * @param in
     * @param out
     */
    private void closeStream(Socket socket, BufferedSource in, BufferedSink out) {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (in != null) {
            try {
                in.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if (out != null) {
            try {
                out.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if (socket != null) {
            try {
                socket.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private static String getKey(String ip, int port) {
        return ip + ":" + port;
    }

}
